/*
Copyright [2012] [Maria Eugenia Malagamba]
Copyright [2012] [Leandro Pellegrino]
Copyright [2012] [Julia Gambuzzi]
Copyright [2012] [Ignacio Capodanno]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#pragma once

#include <string>
#include "tinyXML/tinyxml.h"
#include <iostream>
#include "../Log/Log.h"
#include "ConfiguracionPorDefecto.hpp"

using namespace std;
using namespace Logueador;

namespace Parser
{

	class ValidadorXML
	{

	private:

		TiXmlDocument *documento;
		Log *log;
		ConfiguracionPorDefecto *configuracionPorDefecto; 
		string carpetaCliente;

		bool esValidoElNodo(TiXmlElement *nodo, string nombreDelNodo)
		{
			if (nodo == 0)
			{
				this->log->escribir("ERROR: El nodo " + nombreDelNodo + " no existe y es obligatorio");
				return false;
			}

			return true;
		}

		bool esValidoElAtributoString(string valorDelAtributo, string nombreDelAtributo, int fila, int columna)
		{
			if (valorDelAtributo.empty())
			{
				this->log->escribir("ERROR: El atributo " + nombreDelAtributo + " no tiene un valor válido o no existe", fila, columna);
				return false;
			}

			return true;
		}

		bool esValidoElAtributoEntero(string valorDelAtributo, string nombreDelAtributo, int fila, int columna)
		{
			if (valorDelAtributo.empty())
			{
				this->log->escribir("ERROR: El atributo " + nombreDelAtributo + " no tiene un valor válido o no existe", fila, columna);
				return false;
			}

			if (valorDelAtributo != "0" && atoi(valorDelAtributo.c_str()) <= 0 )
			{
				this->log->escribir("ERROR: El atributo " + nombreDelAtributo + " debe ser numérico y mayor o igual que cero.", fila, columna);
				return false;
			}

			return true;
		}

		bool esValidoElAtributoDouble(string valorDelAtributo, string nombreDelAtributo, int fila, int columna)
		{
			if (valorDelAtributo.empty())
			{
				this->log->escribir("ERROR: El atributo " + nombreDelAtributo + " no tiene un valor válido o existe", fila, columna);
				return false;
			}

			if (valorDelAtributo != "0" && atof(valorDelAtributo.c_str()) == (double)0.0)
			{
				this->log->escribir("ERROR: El atributo " + nombreDelAtributo + " no tiene un valor válido", fila, columna);
				return false;
			}

			return true;
		}

		bool esValidoElAtributoImagen(string valorDelAtributo, string nombreDelAtributo, int fila, int columna)
		{
			if (!valorDelAtributo.empty()){
				FILE *imagen = fopen(valorDelAtributo.c_str(), "r");
				if(NULL == imagen){
					this->log->escribir("ERROR: No se ha encontrado el archivo de la imagen", fila, columna);
					return false;
				}else{
					fclose(imagen);
					return true;
				}
			}else{
				this->log->escribir("ERROR: El atributo " + nombreDelAtributo + " no tiene un valor válido", fila, columna);
				return false;
			}
		}

	public:

		ValidadorXML(TiXmlDocument *documento, string carpetaCliente = "")
		{
			this->documento = documento;
			this->carpetaCliente = carpetaCliente;
			this->log = Log::getInstanciaValidador();
			this->configuracionPorDefecto = new ConfiguracionPorDefecto();
		}

		void chequearNodo(TiXmlElement *padre, TiXmlElement* &nodo, string nombreDelNodo)
		{
			this->log->escribir("INFO: Validando Nodo " + nombreDelNodo);

			if (!this->esValidoElNodo(nodo, nombreDelNodo))
			{
				nodo = new TiXmlElement(nombreDelNodo.c_str());

				if (padre != 0)
					padre->LinkEndChild(nodo);
				else
					this->documento->LinkEndChild(nodo);
			}
		}

		void chequearAtributoEntero(TiXmlElement *nodo, string nombreDelAtributo, string valorPorDefecto)
		{
			//Chequeo si existe el atributo
			if(nodo->Attribute(nombreDelAtributo.c_str()) != NULL){

				string valor = nodo->Attribute(nombreDelAtributo.c_str());

				if (!this->esValidoElAtributoEntero(valor, nombreDelAtributo, nodo->Row(), nodo->Column()))
				{
					nodo->SetAttribute(nombreDelAtributo.c_str(), valorPorDefecto.c_str());
				}
			}else{
				nodo->SetAttribute(nombreDelAtributo.c_str(), valorPorDefecto.c_str());
			}
		}

		void chequearAtributoString(TiXmlElement *nodo, string nombreDelAtributo, string valorPorDefecto)
		{
			//Chequeo si existe el atributo antes de validar
			if(nodo->Attribute(nombreDelAtributo.c_str()) != NULL){
				string valor = nodo->Attribute(nombreDelAtributo.c_str());

				if (!this->esValidoElAtributoString(valor, nombreDelAtributo, nodo->Row(), nodo->Column()))
				{
					nodo->SetAttribute(nombreDelAtributo.c_str(), valorPorDefecto.c_str());
				}
			}else{
				nodo->SetAttribute(nombreDelAtributo.c_str(), valorPorDefecto.c_str());
			}

		}

		void chequearAtributoDouble(TiXmlElement *nodo, string nombreDelAtributo, string valorPorDefecto)
		{
			//Chequeo si existe el atributo antes de validar
			if(nodo->Attribute(nombreDelAtributo.c_str()) != NULL){
				string valor = nodo->Attribute(nombreDelAtributo.c_str());

				if (!this->esValidoElAtributoDouble(valor, nombreDelAtributo, nodo->Row(), nodo->Column()))
				{
					nodo->SetAttribute(nombreDelAtributo.c_str(), valorPorDefecto.c_str());
				}
			}else{
				nodo->SetAttribute(nombreDelAtributo.c_str(), valorPorDefecto.c_str());
			}
		}

		void chequearAtributoImagen(TiXmlElement *nodo, string nombreDelAtributo, string valorPorDefecto)
		{
			//Chequeo si existe el atributo antes de validar
			if(nodo->Attribute(nombreDelAtributo.c_str()) != NULL){
				string valor = nodo->Attribute(nombreDelAtributo.c_str());
				string nombreDelArchivo = this->carpetaCliente + valor;
				
				// temporal fix (solo para las naves que estan en el root)
				if (nombreDelArchivo.find("nave") != string::npos) nombreDelArchivo = valor;

				if (!this->esValidoElAtributoImagen(nombreDelArchivo, nombreDelAtributo, nodo->Row(), nodo->Column()))
				{
					nodo->SetAttribute(nombreDelAtributo.c_str(), valorPorDefecto.c_str());
				}
			}else{
				nodo->SetAttribute(nombreDelAtributo.c_str(), valorPorDefecto.c_str());
			}
		}

		//**** Validaciones lógicas ****
		void chequearAtributosTagAsteroids(TiXmlElement *asteroids){
			if(asteroids->Attribute("ancho") == NULL){
				string anchoPorDefecto = this->configuracionPorDefecto->getAnchoAsteroids();
				asteroids->SetAttribute("ancho", anchoPorDefecto.c_str());
			}
			if(asteroids->Attribute("alto") == NULL){
				string altoPorDefecto = this->configuracionPorDefecto->getAltoAsteroids();
				asteroids->SetAttribute("alto", altoPorDefecto.c_str());
			}
		}

		void chequearAtributosTagTipoAsteroide(TiXmlElement *tipoAsteroide){
			if(tipoAsteroide->Attribute("nombre") == NULL){
				string tipoPorDefecto = this->configuracionPorDefecto->getTipoDeAsteroide();
				tipoAsteroide->SetAttribute("nombre", tipoPorDefecto .c_str());
			}
			if(tipoAsteroide->Attribute("imagen") == NULL){
				string imagenPorDefecto = this->configuracionPorDefecto->getImagenDeAsteroide();
				tipoAsteroide->SetAttribute("imagen", imagenPorDefecto.c_str());
			}
			if(tipoAsteroide->Attribute("ancho") == NULL){
				string anchoPorDefecto = this->configuracionPorDefecto->getAnchoDeTipoDeAsteroide();
				tipoAsteroide->SetAttribute("ancho", anchoPorDefecto.c_str());
			}
			if(tipoAsteroide->Attribute("alto") == NULL){
				string altoPorDefecto = this->configuracionPorDefecto->getAltoDeTipoDeAsteroide();
				tipoAsteroide->SetAttribute("alto", altoPorDefecto.c_str());
			}
			if(tipoAsteroide->Attribute("rotacion") == NULL){
				string rotacionPorDefecto = this->configuracionPorDefecto->getRotacionDeTipoDeAsteroide();
				tipoAsteroide->SetAttribute("rotacion", rotacionPorDefecto.c_str());
			}
		}

		void chequearAtributosTagAsteroide(TiXmlElement *asteroide){
			if(asteroide->Attribute("tipo") == NULL){
				string tipoPorDefecto = this->configuracionPorDefecto->getTipoDeAsteroide();
				asteroide->SetAttribute("tipo", tipoPorDefecto .c_str());
			}
			if(asteroide->Attribute("x") == NULL){
				string xPorDefecto = this->configuracionPorDefecto->getXAsteroide();
				asteroide->SetAttribute("x", xPorDefecto.c_str());
			}
			if(asteroide->Attribute("y") == NULL){
				string yPorDefecto = this->configuracionPorDefecto->getYAsteroide();
				asteroide->SetAttribute("y", yPorDefecto.c_str());
			}
			if(asteroide->Attribute("direccion") == NULL){
				string direccionPorDefecto = this->configuracionPorDefecto->getDireccionAsteroide();
				asteroide->SetAttribute("direccion", direccionPorDefecto.c_str());
			}
			if(asteroide->Attribute("velocidad") == NULL){
				string velocidadPorDefecto = this->configuracionPorDefecto->getVelocidadAsteroide();
				asteroide->SetAttribute("rotacion", velocidadPorDefecto.c_str());
			}
		}

		void chequearAtributosTagNave(TiXmlElement *nave){
			if(nave->Attribute("imagen") == NULL){
				string imagenPorDefecto = this->configuracionPorDefecto->getImagenNave();
				nave->SetAttribute("tipo", imagenPorDefecto .c_str());
			}
			if(nave->Attribute("velRotacion") == NULL){
				string velRotacionPorDefecto = this->configuracionPorDefecto->getVelocidadDeRotacionNave();
				nave->SetAttribute("velRotacion", velRotacionPorDefecto.c_str());
			}
			if(nave->Attribute("velMaxima") == NULL){
				string velMaximaPorDefecto = this->configuracionPorDefecto->getVelocidadMaximaNave();
				nave->SetAttribute("velMaxima", velMaximaPorDefecto.c_str());
			}
			if(nave->Attribute("propulsion") == NULL){
				string propulsionPorDefecto = this->configuracionPorDefecto->getPropulsionNave();
				nave->SetAttribute("propulsion", propulsionPorDefecto.c_str());
			}
			if(nave->Attribute("friccion") == NULL){
				string friccionPorDefecto = this->configuracionPorDefecto->getFriccionNave();
				nave->SetAttribute("friccion", friccionPorDefecto.c_str());
			}
			if(nave->Attribute("duracionBala") == NULL){
				string duracionBalaPorDefecto = this->configuracionPorDefecto->getDuracionBalaNave();
				nave->SetAttribute("duracionBala", duracionBalaPorDefecto.c_str());
			}
			if(nave->Attribute("velocidadBala") == NULL){
				string velocidadBalaPorDefecto = this->configuracionPorDefecto->getVelocidadBalaNave();
				nave->SetAttribute("velocidadBala", velocidadBalaPorDefecto.c_str());
			}
		}

		void chequearAltoAnchoPantalla(TiXmlElement *nodo){
			string ancho = nodo->Attribute("ancho");
			string alto = nodo->Attribute("alto");
			int fila = nodo->Row();
			int columna = nodo->Column();

			if(atoi(ancho.c_str()) < ConfiguracionPorDefecto::getMinAnchoPantalla()){
				nodo->SetAttribute("ancho", ConfiguracionPorDefecto::getMinAnchoPantalla());
				std::stringstream ssMaxAncho;
				ssMaxAncho << ConfiguracionPorDefecto::getMinAnchoPantalla();
				this->log->escribir("ERROR: El ancho de la pantalla debe ser mayor que " + 
					ssMaxAncho.str(), fila, columna);
			}
			if(atoi(ancho.c_str()) > ConfiguracionPorDefecto::getMaxAnchoPantalla()){
				nodo->SetAttribute("ancho", ConfiguracionPorDefecto::getMaxAnchoPantalla());
				std::stringstream ssMaxAncho;
				ssMaxAncho << ConfiguracionPorDefecto::getMaxAnchoPantalla();
				this->log->escribir("ERROR: El ancho de la pantalla debe ser menor que " + 
					ssMaxAncho.str(), fila, columna);
			}
			if(atoi(alto.c_str()) < ConfiguracionPorDefecto::getMinAltoPantalla()){
				nodo->SetAttribute("alto", ConfiguracionPorDefecto::getMinAltoPantalla());
				std::stringstream ssMinAlto;
				ssMinAlto << ConfiguracionPorDefecto::getMinAltoPantalla();
				this->log->escribir("ERROR: El alto de la pantalla debe ser mayor que " + 
					ssMinAlto.str(), fila, columna);
			}
			if(atoi(alto.c_str()) > ConfiguracionPorDefecto::getMaxAltoPantalla()){
				nodo->SetAttribute("alto", ConfiguracionPorDefecto::getMaxAltoPantalla());
				std::stringstream ssMaxAlto;
				ssMaxAlto << ConfiguracionPorDefecto::getMaxAltoPantalla();
				this->log->escribir("ERROR: El alto de la pantalla debe ser menor que " + 
					ssMaxAlto.str(), fila, columna);
			}
		}

		bool chequearAsteoideDentroPantalla(int alto, int ancho, TiXmlElement *nodo){
			int x = atoi(nodo->Attribute("x"));
			int y = atoi(nodo->Attribute("y"));
			if((x > ancho) || (y > alto)){
				return false;
			}else{
				return true;
			}
		}

		bool chequearTamanioAsteroide(int altoPantalla, int anchoPantalla, TiXmlElement *asteroide){
			int ancho = atoi(asteroide->Attribute("ancho"));
			int alto = atoi(asteroide->Attribute("alto"));
			if(ancho > anchoPantalla || alto > altoPantalla){
				return false;
			}else{
				return true;
			}
		}

		~ValidadorXML(){}
	};

}